package com.bxt.demo.task;

import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 整个任务均衡的需求是存在一些不完整性的，例如，任务的产生时间和消费时间没有标定范围。
 * 设备和任务的主次关系没有明确。
 * 任务是产生任务的是否分配设备，还是在设备完成任务之后主动拉取任务？
 * 如果是设备主动拉取任务，则设备轮休的决策不在此逻辑范围内，单独给设备做排班表就可以了。
 * 这样任务拉取的处理逻辑是根据当天的任务数量和设备在线数量来进行计算和均衡。
 * 另外每天的任务是否是可变的，包括，新增任务，取消任务，变更任务等。
 * 需求信息不是特别明确。
 */
@Slf4j
public class TaskBlance {


    /**
     * N个设备，拉取完成M个城市的任务，任务总量A
     * N*4 > A
     * M/2 > M
     * 每个设备每天最多四个任务，最多跨两个城市拉取任务，尽量所有任务都拉取一个城市。
     * 这里的平衡算法，需要计算一个根本性的问题，每个城市的任务数量是不一样的，所以需求设备量也是不一样的
     * 例如 北京  19个任务 杭州8个任务 上海11个任务，天津1个任务。
     * 所以初始化的假设条件N是必须满足最低基数的才能够进行有效分配，M和jobs数量也需要提前规划范围，否认任务均衡这个事本身就成为了一个伪命题。
     * 首先任务应该有一个预分配的预处理机制吧，按照城市，对任务进行分类。
     * 这样可以知道每个城市的任务数量，这样在拉取任务的时候，才可以更合理的分配每一个任务到对应设备。
     * 是否存在任务优先级定义？
     * 北京： 1 2 3 4
     * 上海： 5 6 7
     * 杭州： 8 9 10 11 12 13 14
     * 广州： 15
     *
     * D1：杭州
     * D2：北京
     * D3：上海 广州
     * D4：杭州
     * 这里还存在有一个数据问题，如果M个城市的任务数量，在相邻两天出现巨大波动的时候，也会出现一些问题。
     * 例如第二天杭州没有任务，广州出现7个任务，北京三个，上海5个
     * 然后第三天任务继续变动……这个时候就出问题了，只能增加设备才能解决。
     * 所以从这种逻辑推理而言，没增加一个设备，对于这个设备而言，绑定的城市都是根据数据来进行平衡的。任务增多，当前设备无法满足使用的情况下，需要新增设备，来平衡所有地区。
     * 同理当任务减少后，设备则可以按照冒泡顺序轮休。
     * 通过设备拉取动态或许任务，好处是可以最大限度提高实效性，这样的话任务拉取还得加锁。
     * @param args
     */
    public static void main(String[] args) {
        log.info("d001:{}",getTaskByDeviceId("d001"));
        log.info("d002:{}",getTaskByDeviceId("d002"));
        log.info("d001:{}",getTaskByDeviceId("d001"));
        log.info("d003:{}",getTaskByDeviceId("d003"));
        log.info("d004:{}",getTaskByDeviceId("d004"));
        log.info("d002:{}",getTaskByDeviceId("d002"));
        log.info("d001:{}",getTaskByDeviceId("d001"));
        log.info("d004:{}",getTaskByDeviceId("d004"));
        log.info("d003:{}",getTaskByDeviceId("d003"));
        log.info("d004:{}",getTaskByDeviceId("d004"));
    }

    /**
     * 这里暂时不涉及到设备管理，对于设别的动态扩容和缩容不在这里处理，应该是任务产生的时候调用设备容量管理。
     * 另外设备管理本身也是一个独立模块。包括任务地分配，设备重置，清除任务地等等一系列处理方式
     */
    static ConcurrentHashMap TASK_DATA = new ConcurrentHashMap(10);
    static ConcurrentHashMap DEVICE_DATA = new ConcurrentHashMap(10);
    static String [] CITYS = {"北京","上海","杭州","天津"};
    //初始化一些测试数据，用map来存储测试数据，还是不如用数据库存储处理起来方便
    static {
        Device D1 = new Device("d001","杭州","",0,new Date(),0);
        Device D2 = new Device("d002","北京","",0,new Date(),0);
        Device D3 = new Device("d003","","",0,new Date(),0);
        Device D4 = new Device("d004","","",0,new Date(),0);
        DEVICE_DATA.put("d001",D1);
        DEVICE_DATA.put("d002",D2);
        DEVICE_DATA.put("d003",D3);
        DEVICE_DATA.put("d004",D4);
        List<Task> taskList = new ArrayList<>();
        for (int i=0;i<5;i++) {
            Task t = new Task(i, "北京",new Date(),0);
            taskList.add(t);
        }
        City city = new City("北京",5,taskList);
        //北京初始化5个任务
        TASK_DATA.put("北京",city);
        List<Task> taskList1 = new ArrayList<>();
        for (int i=5;i<9;i++) {
            Task t = new Task(i, "上海",new Date(),0);
            taskList1.add(t);
        }
        City city1 = new City("上海",4,taskList1);
        TASK_DATA.put("上海",city1);
        List<Task> taskList2 = new ArrayList<>();
        for (int i=9;i<12;i++) {
            Task t = new Task(i, "杭州",new Date(),0);
            taskList2.add(t);
        }
        City city2 = new City("杭州",3,taskList2);
        TASK_DATA.put("杭州",city2);
        List<Task> taskList3 = new ArrayList<>();
        for (int i=12;i<14;i++) {
            Task t = new Task(i, "天津",new Date(),0);
            taskList3.add(t);
        }
        City city3 = new City("天津",4,taskList3);
        TASK_DATA.put("天津",city3);
    }

    /**
     * 根据设备ID获取任务ID，实质流程就是先根据设备ID查询设备信息，然后根据设备信息，获取对应城市的任务信息，由多到少抽取任务。
     * 如果设备本身未绑定任务城市，则从剩余任务中从多到少抽取一个城市并绑定，然后抽取任务，抽取任务后改变相关状态。
     * @return
     */
    public synchronized static int getTaskByDeviceId(String deviceId){
        if (DEVICE_DATA.containsKey(deviceId)) {
            Device d = (Device) DEVICE_DATA.get(deviceId);
            if (d.getCityPrimary() == "") {
                //没有首选城市，获取当前任务剩余任务最多的城市作为首选城市并返回任务ID，这里如果无法获取任务则说明任务为空
                String city = "";
                int nunTemp = 0;
                for (String cityName : CITYS) {
                    City c = (City) TASK_DATA.get(cityName);
                    if (c.getJobCount() > nunTemp) {
                        nunTemp = c.getJobCount();
                        city = cityName;
                    }
                }
                d.setCityPrimary(city);
                City c = (City) TASK_DATA.get(city);
                int count = c.getJobCount();
                if (count > 0) {
                    Task t = c.getJobs().get(0);
                    int ret = t.getTaskId();
                    c.setJobCount(count-1);
                    c.getJobs().remove(0);
                    return  ret;
                } else {
                    return -2;
                }
            } else {
                //有首选城市，则取首选城市获取任务,如果首选城市没有任务，则选次选城市。
                City c = (City) TASK_DATA.get(d.getCityPrimary());
                if (c.getJobCount()>0) {
                    Task t = c.getJobs().get(0);
                    int ret = t.getTaskId();
                    c.setJobCount(c.getJobCount()-1);
                    c.getJobs().remove(0);
                    return  ret;
                }
                //如果首选城市任务为空，则判断次选城市。
                if (d.getCitySecond() == "") {
                    String city = "";
                    int nunTemp = 0;
                    for (String cityName : CITYS) {
                        City city1 = (City) TASK_DATA.get(cityName);
                        if (city1.getJobCount() > nunTemp) {
                            nunTemp = city1.getJobCount();
                            city = cityName;
                        }
                    }
                    d.setCitySecond(city);
                    City city1 = (City) TASK_DATA.get(city);
                    int count = city1.getJobCount();
                    if (count > 0) {
                        Task t = city1.getJobs().get(0);
                        int ret = t.getTaskId();
                        city1.setJobCount(count-1);
                        city1.getJobs().remove(0);
                        return  ret;
                    } else {
                        return -2;
                    }
                } else {
                    //如果有次选城市，则获取次选城市任务。
                    City city = (City) TASK_DATA.get(d.getCitySecond());
                    if (city.getJobCount()>0) {
                        Task t = city.getJobs().get(0);
                        int ret = t.getTaskId();
                        city.setJobCount(city.getJobCount()-1);
                        city.getJobs().remove(0);
                        return  ret;
                    } else {
                        return -2;
                    }
                }
            }
        } else {
            //当前设备不存在。 -2无剩余任务。
            return -1;
        }
    }
}
